package minidb.record;

import java.io.IOException;

import minidb.buffer.Buffer;
import minidb.file.*;
import static minidb.type.Const.*;

public class TablePage {
	private FileMgr fMgr;
	private java.nio.ByteBuffer tab = java.nio.ByteBuffer.allocateDirect(PAGE_SIZE);
	public java.util.HashMap<String, TableInfo> table;
	private byte[] lock = new byte[0];

	public TablePage() throws IOException, ClassNotFoundException{
		this.fMgr = dbFile;
		table = new java.util.HashMap<String, TableInfo>();
		fMgr.read(0, tab);
		tab.position(FREE);
		int maxpos = tab.getInt();
		TableInfo tmp = null;
		while(true){
			if(tab.position()>=maxpos){
				if(tab.getInt(NEXT_PAGE)==0)
					break;
				fMgr.read(tab.getInt(NEXT_PAGE),tab);
				tab.position(FREE);
				maxpos = tab.getInt();
			}
			tmp = new TableInfo(tab);
			table.put(tmp.tablename(), tmp);
			
		}
	}

	public void addTable(TableInfo tableInfo){
		synchronized(lock){
			if(table.containsKey(tableInfo.tablename()))
				throw new RuntimeException("Table " + tableInfo.tablename() 
						+ " has already exists!");
			tableInfo.startpage(fMgr.allocPage());
			tableInfo.indexPage(fMgr.allocPage());
			Buffer buf = bufpool.pin(tableInfo.startpage());
			buf.putInt(PRE_PAGE, 0);
			buf.putInt(FREE, INT_SIZE * 5);
			buf.putInt(FREEPAGE, 0);
			bufpool.unpin(buf);
			table.put(tableInfo.tablename(), tableInfo);
			byte []bt = tableInfo.array();
			fMgr.read(0,tab);
			while(true){
				if(tab.getInt(FREE) + bt.length <= PAGE_SIZE){
					tab.position(tab.getInt(FREE));
					tab.put(bt);
					tab.putInt(FREE,tab.position());
					tab.rewind();
					fMgr.write(tab.getInt(CURRENT_PAGE),tab);
					break;
				}
				else{
					if(tab.getInt(0)!=0)
						fMgr.read(tab.getInt(0),tab);
					else{
						int newPage = fMgr.allocPage();
						int oldPage = tab.getInt(CURRENT_PAGE);
						tab.putInt(NEXT_PAGE,newPage);
						fMgr.write(oldPage,tab);
						tab.rewind();
						tab.putInt(oldPage);
						tab.position(INT_SIZE*4);
						tab.put(bt);
						tab.putInt(FREE,tab.position());
						tab.rewind();
						fMgr.write(newPage,tab);
						break;
					}
				}
			}
		}
	}
	
	public void deleteTable(String tablename) throws IOException, ClassNotFoundException{
		synchronized(lock){
			if(!table.containsKey(tablename)){
				throw new RuntimeException("No Such Tale " + tablename);
			}
			TableInfo tableInfo = table.get(tablename);
			table.remove(tableInfo.tablename());
			rewind();
			TableInfo tmp;
			while((tmp = next())!=null){
				if(tmp.tablename().equals(tableInfo.tablename())){
					int size = tab.getInt(12) - tab.position();
					int pos = tab.position()-tmp.array().length;
					if(size==0){
						tab.putInt(12, pos);
						
					}
					else{
						byte []bt = new byte[size];
						tab.get(bt);
						tab.position(pos);
						tab.put(bt);
						tab.putInt(12, tab.position());
					}
					tab.rewind();
					fMgr.write(tab.getInt(4), tab);
					tab.position(pos);
					break;
				}
			}
		}
	}
	
	public TableInfo next() throws IOException, ClassNotFoundException{
		int maxpos = tab.getInt(12);
		if(tab.position() >= maxpos){
			if(tab.getInt(0) == 0)
				return null;
			fMgr.read(tab.getInt(0), tab);
			tab.position(16);
		}
		return new TableInfo(tab);
	}
	
	public TableInfo get(String tablename){
		synchronized(lock){
			if(!table.containsKey(tablename))
				throw new RuntimeException("NO Such table: " + tablename);
			return table.get(tablename);
		}
	}
	
	public Schema schema(String tablename){
		synchronized(lock){
			if(!this.table.containsKey(tablename))
				return null;
			return this.table.get(tablename).schema();
		}
	}
	
	public void rewind(){
		fMgr.read(0, tab);
		tab.position(16);
	}
}
